﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace Game
{
    class WinAPI
    {
        public enum Bits : int
        {
            b0,
            b1,
            b2,
            b3,
            b4,
            b5,
            b6,
            b7,
            b8,
            b9,
            b10,
            b11,
            b12,
            b13,
            b14,
            b15,
            b16,
            b17,
            b18,
            b19,
            b20,
            b21,
            b22,
            b23,
            b24,
            b25,
            b26,
            b27,
            b28,
            b29,
            b30,
            b31,
        }

        public enum ShowWindowCommand : int
        {
            Hide = 0,
            Normal = 1,
            ShowMinimized = 2,
            Maximize = 3, // is this the right value?     
            ShowMaximized = 3,
            ShowNoActivate = 4,
            Show = 5,
            Minimize = 6,
            ShowMinNoActive = 7,
            ShowNA = 8,
            Restore = 9,
            ShowDefault = 10,
            ForceMinimize = 11
        }

        public enum MessageCode : int
        {
            Create = 1,
            Destroy = 2,
            Show = 5,
            SetFocus = 7,
            KillFocus = 8,
            KeyDown = 256,
            KeyUp = 257,
            MouseLeftDown = 0x0201,
            MouseLeftUp = 0x0202,
            MouseRightDown = 0x0204,
            MouseRightUp = 0x0205
        }

        public enum WindowStyle : long
        {
            Visible = 0x10000000L,
            Popup = 0x80000000L
        }

        public enum MessageRemove
        {
            No = 0,
            Yes = 1
        }

        public struct Point
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct Message
        {
            public IntPtr handle;
            public uint msg;
            public IntPtr wParam;
            public IntPtr lParam;
            public uint time;
            public Point p;
        }

        [DllImport("user32.dll")]
        public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow);
        [DllImport("user32.dll")]
        public static extern bool UpdateWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool PeekMessage(out Message lpMsg, HandleRef hWnd, uint MsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool TranslateMessage(ref Message lpMsg);

        [DllImport("user32.dll")]
        public static extern IntPtr DispatchMessage(ref Message lpMsg);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetCursorPos(out Point lpPoint);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetCursorPos(int x, int y);

    }


    public class Application : NativeWindow, Events.IUserInputListener
    {
        private float mTimeElapsed = 1.0f / 30.0f;
        private float mTimePerSecond = 0;
        private int mUpdatePerSecond = 0;
        private bool mActive;
        private bool mAlive;
        private IDK.Vector2 mMouseMove;
        private CreateParams mWindowParam = new CreateParams();

        public Application() : base()
        {
            mActive = true;
            mAlive = false;

            Log.Hint(String.Format("Aplication creating.."));


            mWindowParam.Caption = "Application";
            mWindowParam.Width = 1024;
            mWindowParam.Height = 768;
            //setcursor
            WinAPI.SetCursorPos(mWindowParam.Width / 2, mWindowParam.Height / 2);

            mWindowParam.X = 0;
            mWindowParam.Y = 0;

            long style = (long)WinAPI.WindowStyle.Visible;
            style |= (long)WinAPI.WindowStyle.Popup;
            mWindowParam.Style = (int)style;
            mWindowParam.Parent = IntPtr.Zero;
        }

        public CreateParams WindowParam
        {
            get { return mWindowParam; }
        }

        public void Terminated()
        {
            mAlive = false;
        }

        public bool IsMouseMove()
        {
            return mMouseMove.x != 0 || mMouseMove.y != 0;
        }

        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        protected override void WndProc(ref Message msg)
        {
            switch ((WinAPI.MessageCode)msg.Msg)
            {
             case WinAPI.MessageCode.MouseLeftDown:
                    OnKeyDown(GenerateMouseControlInput(Events.UserInput.Key.MouseLeft));
                    break;

                case WinAPI.MessageCode.MouseRightDown:
                    OnKeyDown(GenerateMouseControlInput(Events.UserInput.Key.MouseRight));
                    break;
                    
                case WinAPI.MessageCode.MouseLeftUp:
                    OnKeyUp(GenerateControlInput(Events.UserInput.Key.MouseLeft));
                    break;

                case WinAPI.MessageCode.MouseRightUp:
                    OnKeyUp(GenerateControlInput(Events.UserInput.Key.MouseRight));
                    break;
  
                case WinAPI.MessageCode.Destroy:
                    mAlive = false;
                    break;
                case WinAPI.MessageCode.SetFocus:
                    Log.Warn("SetFocus");
                    if (mAlive)
                    {
                        Active(true);
                    }
                    break;
                case WinAPI.MessageCode.KillFocus:
                    Log.Warn("KillFocus");
                    if (mAlive)
                    {
                        Active(false);
                    }
                    break;
                case WinAPI.MessageCode.KeyDown:
                    if ((msg.LParam.ToInt64() & 0x40000000) != 0x40000000) //ignore autorepeat
                    {
                        OnKeyDown(GenerateControlInput((Events.UserInput.Key)(msg.WParam.ToInt32())));
                    }
                    break;
                case WinAPI.MessageCode.KeyUp:
                    OnKeyUp(GenerateControlInput((Events.UserInput.Key)(msg.WParam.ToInt32())));
                    break;
            }
            base.WndProc(ref msg);
        }


        protected Events.MouseUserInput GenerateMouseControlInput(Events.UserInput.Key key)
        {
            return new Events.MouseUserInput(mMouseMove, key);
        }

        protected Events.UserInput GenerateControlInput(Events.UserInput.Key key)
        {
            return new Events.UserInput(key);
        }

        public void Active(bool active)
        {
            Log.Hint(String.Format("Aplication Active = {0}", active));
            mActive = active;
            OnActive(active);

            mTimeElapsed = 1.0f / 30.0f;
            mTimePerSecond = 0;
            mUpdatePerSecond = 0;
        }

        public void Update(Events.ProcessUpdate evnt)
        {
            WinAPI.Point pos;
            WinAPI.GetCursorPos(out pos);
            mMouseMove.x = mWindowParam.Width / 2 - pos.x;
            mMouseMove.y = mWindowParam.Height / 2 - pos.y;
            
            //Set Cursor Position
            WinAPI.SetCursorPos(mWindowParam.Width / 2, mWindowParam.Height / 2);

            if (IsMouseMove())
            {
                OnMouseMove(new Events.MouseUserInput(mMouseMove, Events.UserInput.Key.None));
            }


            if (OnUpdate(evnt) == false)
            {
                mAlive = false;
            }
        }

        public void Run()
        {

            Log.Hint(String.Format("Create window"));

            this.CreateHandle(mWindowParam);

            OnCreate();
            //
            Active(mAlive = true);

            Log.Hint(String.Format("Show window"));

            WinAPI.ShowWindow(Handle, WinAPI.ShowWindowCommand.Show);

            WinAPI.UpdateWindow(Handle);

            HandleRef hWnd = new HandleRef(this, Handle);

            IDK.Timer timer = new IDK.Timer();
            WinAPI.Message msg;

            Events.ProcessUpdate evntUpdate = new Events.ProcessUpdate();
            evntUpdate.ElapsedTime = 0;// 1.0f / 30.0f;


            mTimeElapsed = 0;
            while (mAlive)
            {
                while (WinAPI.PeekMessage(out msg, hWnd, 0, 0, (int)WinAPI.MessageRemove.Yes))
                {
                    WinAPI.TranslateMessage(ref msg);
                    WinAPI.DispatchMessage(ref msg);
                }

                if (mActive)
                {
                    float MinTimeStep = 1.0f / 10.0f;
                    float TimeStep = 1.0f / 50.0f;

                    float dt = timer.GetElapsed();

                    if (dt > MinTimeStep)
                    {
                        dt = MinTimeStep;
                    }

                    mTimeElapsed += dt;

                    mTimePerSecond += dt;

                    if (mTimePerSecond > 1)
                    {
                        //Log.Hint("UpdatePerSecond:" + mUpdatePerSecond.ToString() + " " + mTimePerSecond.ToString());
                        mUpdatePerSecond = 0;
                        mTimePerSecond = 0;
                    }

                    if (mTimeElapsed > TimeStep)
                    {
                        evntUpdate.ElapsedTime = mTimeElapsed;
                        Update(evntUpdate);
                        mTimeElapsed = 0;
                        ++mUpdatePerSecond;
                    }
                    else
                    {
                        float timeSleep = (TimeStep - mTimeElapsed) * 1000.0f;
                        //Log.Hint(timeSleep.ToString());
                        timer.Sleep((uint)(timeSleep));
                    }
                }
                else
                {
                    timer.Sleep(10);
                }
            }

            OnDestory();
        }

        protected virtual void OnCreate()
        {
        }

        protected virtual bool OnUpdate(Events.ProcessUpdate evnt)
        {
            return true;
        }

        protected virtual void OnDestory()
        {
            ReleaseHandle();
        }

        protected virtual void OnActive(bool value)
        {
        }

        #region IUserInputListener Members

        public virtual void OnKeyDown(Events.UserInput input)
        {
            throw new NotImplementedException();
        }

        public virtual void OnKeyUp(Events.UserInput input)
        {
            throw new NotImplementedException();
        }

        public virtual void OnMouseMove(Events.MouseUserInput input)
        {
            throw new NotImplementedException();
        }

        public virtual void OnMouseDown(Events.MouseUserInput input)
        {
            throw new NotImplementedException();
        }

        public virtual void OnMouseUp(Events.MouseUserInput input)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}